package main

import (
	"dingusxp/trial/test/util/mprotect"
	"fmt"
	"reflect"
	"runtime"
	"unsafe"
)

func main() {

	// TestInt()
	// TestStringHeader()
	// TestSliceRefs()
	// TestStructMemoryAlignment()
	TestStructEmbedded()
}

// 取内存地址通过修改byte修改int的值
// 同时表明Go语言中整形是小端模式
func TestInt() {

	n := uint32(0x88888888)
	fmt.Printf("n=%d, address=0x%x\n", n, &n)
	bytes := (*[4]byte)(unsafe.Pointer(&n))
	fmt.Printf("bytes=%#v, address=0x%x\n", bytes, &bytes[0])
	bytes[0] = byte(0xff)
	bytes[1] = byte(0x00)
	bytes[2] = byte(0x00)
	bytes[3] = byte(0x00)
	fmt.Printf("bytes=%#v, n=%d\n", bytes, n)
}

// string 的实现，StringHeader 结构
func TestStringHeader() {

	s := "hello, world"
	sp := (*reflect.StringHeader)(unsafe.Pointer(&s))
	fmt.Printf("real address of string(%s)=0x%x\n", s, sp.Data)

	fmt.Printf("get string chars by directly reading memory\n")
	chars := (*[12]byte)(unsafe.Pointer(sp.Data))
	fmt.Printf("address of chars[0]: 0x%x\n", &chars[0])
	for k, c := range chars {
		fmt.Printf("  %02d=%c (ascii=%d)\n", k, c, c)
	}

	// 修改字符串值

	// 虚假修改：借助切片
	bytes := []byte(s) // 涉及中文可用  words := []rune(s)
	bytes[7] = 'G'
	bytes[8] = 'o'
	bytes[9] = '.'
	bytes[10] = '.'
	bytes[11] = '.'
	fmt.Printf("bytes2string: %s ; origin string: %s\n", string(bytes), s)

	// 另一种修改，效率更高
	fmt.Printf("string operation: %s ; origin string: %s\n", s[0:7]+"Go...", s)

	// 真实修改字符串内存
	// 直接尝试修改对应值，会失败。。。immutable
	// s[7] = 'G' // 编译时报错
	// chars[7] = 'G' // 运行时报错

	// 注意：以下仅测试，千万不可以在实际项目中应用！
	// 解开束缚（only linux & macos）
	if os := runtime.GOOS; os != "linux" && os != "darwin" {
		fmt.Printf("mprotect does not work for %s\n", os)
		return
	}
	mprotect.ModifyProtection(sp.Data, true)
	chars[7] = 'G'
	chars[8] = 'o'
	chars[9] = '.'
	chars[10] = '.'
	chars[11] = '.'
	fmt.Printf("string now: %s\n", s)
	mprotect.ModifyProtection(sp.Data, false)
}

// 测试切片引用数组地址，在自动扩容时会变化
// slice { array unsafe.Pointer, len int, cap int }
func TestSliceRefs() {

	arr := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	s := arr[5:7]
	fmt.Printf("init: s=arr[5:7]\n")
	fmt.Printf("arr(%#v), &arr[5]=0x%x\n", arr, &arr[5])
	fmt.Printf("slice s(%#v) info: pointer=0x%x, len=%d, cap=%d\n", s, *(*uintptr)(unsafe.Pointer(&s)), len(s), cap(s))

	s = append(s, 88)
	s[0] = 66
	fmt.Printf("\noperation: s.apend(88), s[0]=66 \n")
	fmt.Printf("arr(%#v), &arr[5]=0x%x\n", arr, &arr[5])
	fmt.Printf("slice s(%#v) info: pointer=0x%x, len=%d, cap=%d\n", s, *(*uintptr)(unsafe.Pointer(&s)), len(s), cap(s))

	s = append(s, 99)
	s[0] = 666
	fmt.Printf("\noperation: s.apend(99), s[0]=6666 \n")
	fmt.Printf("arr(%#v), &arr[5]=0x%x\n", arr, &arr[5])
	fmt.Printf("slice s(%#v) info: pointer=0x%x, len=%d, cap=%d\n", s, *(*uintptr)(unsafe.Pointer(&s)), len(s), cap(s))

	s = append(s, 100)
	s[0] = 6666
	fmt.Printf("\noperation: s.apend(100), s[0]=6666 \n")
	fmt.Printf("arr(%#v), &arr[5]=0x%x\n", arr, &arr[5])
	fmt.Printf("slice s(%#v) info: pointer=0x%x, len=%d, cap=%d\n", s, *(*uintptr)(unsafe.Pointer(&s)), len(s), cap(s))
}

// 测试 结构体的内存对齐
// 只是field顺序不同，结构体size可能就不同了
func TestStructMemoryAlignment() {

	type T1 struct {
		B uint8
		U uint16
		I uint64
	}

	type T2 struct {
		B uint8
		I uint64
		// B2 uint8
		// _ uint64
		U uint16
	}
	t1 := T1{B: 0}
	t2 := T2{B: 0}
	fmt.Printf("size of t1(%#v): %d\n", t1, unsafe.Sizeof(t1))
	fmt.Printf("size of t2(%#v): %d\n", t2, unsafe.Sizeof(t2))
}

func TestStructEmbedded() {

	type T1 struct {
		B uint8
		U uint16
		I uint64
	}
	type T2 struct {
		T1
	}
	type T3 struct {
		T1 T1
	}
	t1 := T1{B: 0}
	t2 := T2{T1{B: 0}}
	t3 := T3{T1: T1{B: 0}}
	fmt.Printf("size of t1(%#v): %d\n", t1, unsafe.Sizeof(t1))
	fmt.Printf("size of t2(%#v): %d\n", t2, unsafe.Sizeof(t2))
	fmt.Printf("size of t3(%#v): %d\n", t3, unsafe.Sizeof(t3))
}
